<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="nsBM8" id="nsBM8"><span data-lake-id="u0d9597a1" id="u0d9597a1">典型回答</span></h1>
  <p data-lake-id="u7617ece5" id="u7617ece5"><br></p>
  <p data-lake-id="u2e022fc9" id="u2e022fc9"><code data-lake-id="u5507b982" id="u5507b982"><strong><span data-lake-id="ud15c8113" id="ud15c8113">COUNT(1)</span></strong></code><strong><span data-lake-id="ucbc640d3" id="ucbc640d3"> 和 </span></strong><code data-lake-id="u422a8798" id="u422a8798"><strong><span data-lake-id="ueb06c249" id="ueb06c249">COUNT(*)</span></strong></code><strong><span data-lake-id="u7a564f3c" id="u7a564f3c">表示的是直接查询符合条件的数据库表的行数。而</span></strong><code data-lake-id="ub4d93936" id="ub4d93936"><strong><span data-lake-id="u2ba8d16a" id="u2ba8d16a">COUNT(列名)</span></strong></code><strong><span data-lake-id="u785791bd" id="u785791bd">表示的是查询符合条件的列的值不为NULL的行数。</span></strong></p>
  <p data-lake-id="u2a1478bc" id="u2a1478bc"><br></p>
  <p data-lake-id="u718acefa" id="u718acefa"><span data-lake-id="u3ab723cb" id="u3ab723cb">除了查询得到结果集有区别之外，在性能方面</span><code data-lake-id="u487c1ec6" id="u487c1ec6"><span data-lake-id="u16f54245" id="u16f54245">COUNT(*)</span></code><span data-lake-id="u2eff1ac1" id="u2eff1ac1">是约等于</span><code data-lake-id="udf0ee684" id="udf0ee684"><span data-lake-id="ud373c10d" id="ud373c10d">COUNT(1)</span></code><span data-lake-id="ucd60e03f" id="ucd60e03f">的，但是</span><code data-lake-id="uc0bff488" id="uc0bff488"><span data-lake-id="uccf8cca9" id="uccf8cca9">COUNT(*</span><em><span data-lake-id="uc85b0ff8" id="uc85b0ff8">)</span></em></code><em><span data-lake-id="u9be45f66" id="u9be45f66">是SQL92定义的标准统计行数的语法，因为他是标准语法，所以MySQL数据库对他进行过很多优化。</span></em></p>
  <h1 data-lake-id="CJHfI" id="CJHfI"><span data-lake-id="uee50203b" id="uee50203b">扩展知识</span></h1>
  <p data-lake-id="u524506c4" id="u524506c4"><br></p>
  <h2 data-lake-id="soUX8" id="soUX8"><span data-lake-id="u6528e235" id="u6528e235">COUNT</span></h2>
  <p data-lake-id="ubd124da3" id="ubd124da3"><br></p>
  <p data-lake-id="u65b73e36" id="u65b73e36"><span data-lake-id="ub0745d47" id="ub0745d47">关于COUNT函数，在MySQL官网中有详细介绍：</span></p>
  <p data-lake-id="ud6c9e6b0" id="ud6c9e6b0"><br></p>
  <p data-lake-id="u6d75a7b6" id="u6d75a7b6"><img src="http://www.hollischuang.com/wp-content/uploads/2019/10/15709530990912.jpg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_34%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="udd7fb7ad" id="udd7fb7ad">​</span></p>
  <p data-lake-id="uab1d7cb5" id="uab1d7cb5"><br></p>
  <p data-lake-id="ubf36ab25" id="ubf36ab25"><span data-lake-id="u7ba706c2" id="u7ba706c2">简单翻译一下：</span></p>
  <p data-lake-id="uecf4d887" id="uecf4d887"><br></p>
  <p data-lake-id="u3759ef3d" id="u3759ef3d"><span data-lake-id="ud1e413c0" id="ud1e413c0">1、COUNT(expr) ，返回SELECT语句检索的行中expr的值不为NULL的数量。结果是一个BIGINT值。</span></p>
  <p data-lake-id="u44977e43" id="u44977e43"><br></p>
  <p data-lake-id="ud2c6cfc4" id="ud2c6cfc4"><span data-lake-id="u2768eb87" id="u2768eb87">2、如果查询结果没有命中任何记录，则返回0</span></p>
  <p data-lake-id="uc32dbaad" id="uc32dbaad"><br></p>
  <p data-lake-id="u6437eda4" id="u6437eda4"><span data-lake-id="ueb3cbcd1" id="ueb3cbcd1">3、但是，值得注意的是，</span><code data-lake-id="u7e0bbd2a" id="u7e0bbd2a"><span data-lake-id="u67b2d669" id="u67b2d669">COUNT(*)</span></code><span data-lake-id="u0c04cc98" id="u0c04cc98"> 的统计结果中，会包含值为NULL的行数。</span></p>
  <p data-lake-id="udc406b54" id="udc406b54"><br></p>
  <p data-lake-id="u76f0634f" id="u76f0634f"><span data-lake-id="uc54061d6" id="uc54061d6">即以下表记录</span></p>
  <p data-lake-id="u2e3b4851" id="u2e3b4851"><br></p>
  <pre lang="java"><code>
create table #bla(id int,id2 int)
insert #bla values(null,null)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,1)
insert #bla values(1,null)
insert #bla values(null,null)
</code></pre>
  <p data-lake-id="u4f82708e" id="u4f82708e"><br></p>
  <p data-lake-id="u0c1b7eb0" id="u0c1b7eb0"><span data-lake-id="u6db5ad62" id="u6db5ad62">使用语句count(*),count(id),count(id2)查询结果如下：</span></p>
  <p data-lake-id="u90d50205" id="u90d50205"><br></p>
  <pre lang="java"><code>
select count(*),count(id),count(id2)
from #bla
results 7 3 2
</code></pre>
  <p data-lake-id="ue2d2a930" id="ue2d2a930"><br></p>
  <h2 data-lake-id="Jut2i" id="Jut2i"><span data-lake-id="ucbcbeb8b" id="ucbcbeb8b">COUNT(*)的优化</span></h2>
  <p data-lake-id="uf1c8bd6a" id="uf1c8bd6a"><br></p>
  <p data-lake-id="u3cb52915" id="u3cb52915"><span data-lake-id="u6635f017" id="u6635f017">前面提到了</span><code data-lake-id="u7549d690" id="u7549d690"><span data-lake-id="ue5359a6e" id="ue5359a6e">COUNT(*)</span></code><span data-lake-id="uedf9c7fc" id="uedf9c7fc">是SQL92定义的标准统计行数的语法，所以MySQL数据库对他进行过很多优化。那么，具体都做过哪些事情呢？</span></p>
  <p data-lake-id="u90dded1f" id="u90dded1f"><br></p>
  <p data-lake-id="u7b77dd07" id="u7b77dd07"><span data-lake-id="udbcf880b" id="udbcf880b">这里的介绍要区分不同的执行引擎。MySQL中比较常用的执行引擎就是InnoDB和MyISAM。</span></p>
  <p data-lake-id="uff0772a4" id="uff0772a4"><br></p>
  <p data-lake-id="uc8977311" id="uc8977311"><span data-lake-id="ubc5b4463" id="ubc5b4463">MyISAM和InnoDB有很多区别，其中有一个关键的区别和我们接下来要介绍的</span><code data-lake-id="u5069278c" id="u5069278c"><span data-lake-id="u777ec511" id="u777ec511">COUNT(*)</span></code><span data-lake-id="u4be57fd3" id="u4be57fd3">有关，那就是</span><strong><span data-lake-id="u03a05b4a" id="u03a05b4a">MyISAM不支持事务，MyISAM中的锁是表级锁；而InnoDB支持事务，并且支持行级锁。</span></strong></p>
  <p data-lake-id="ueb3ebfb0" id="ueb3ebfb0"><br></p>
  <p data-lake-id="u86769ced" id="u86769ced"><span data-lake-id="u211f6871" id="u211f6871">因为MyISAM的锁是表级锁，所以同一张表上面的操作需要串行进行，所以，MyISAM做了一个简单的优化，那就是它可以把表的总行数单独记录下来，如果从一张表中使用COUNT(*</span><em><span data-lake-id="ufd8f33cb" id="ufd8f33cb">)进行查询的时候，可以直接返回这个记录下来的数值就可以了，当然，前提是不能有where条件。</span></em></p>
  <p data-lake-id="uc4bc8815" id="uc4bc8815"><br></p>
  <p data-lake-id="u2feac28d" id="u2feac28d"><span data-lake-id="u8196b71e" id="u8196b71e">MyISAM之所以可以把表中的总行数记录下来供COUNT(*)查询使用，那是因为MyISAM数据库是表级锁，不会有并发的数据库行数修改，所以查询得到的行数是准确的。</span></p>
  <p data-lake-id="ub66cc3ee" id="ub66cc3ee"><br></p>
  <p data-lake-id="ufb3ea1bf" id="ufb3ea1bf"><span data-lake-id="u00415c62" id="u00415c62">但是，对于InnoDB来说，就不能做这种缓存操作了，因为InnoDB支持事务，其中大部分操作都是行级锁，所以可能表的行数可能会被并发修改，那么缓存记录下来的总行数就不准确了。</span></p>
  <p data-lake-id="u0389f42e" id="u0389f42e"><br></p>
  <p data-lake-id="u7e86a011" id="u7e86a011"><span data-lake-id="uee1ce95d" id="uee1ce95d">但是，InnoDB还是针对COUNT(*)语句做了些优化的。</span></p>
  <p data-lake-id="u2cf4be89" id="u2cf4be89"><br></p>
  <p data-lake-id="u0222841c" id="u0222841c"><span data-lake-id="u0bbd56eb" id="u0bbd56eb">在InnoDB中，使用COUNT(*)查询行数的时候，不可避免的要进行扫表了，那么，就可以在扫表过程中下功夫来优化效率了。</span></p>
  <p data-lake-id="ufdaf7db1" id="ufdaf7db1"><br></p>
  <p data-lake-id="u0d06be0c" id="u0d06be0c"><span data-lake-id="u920279df" id="u920279df">从MySQL 8.0.13开始，针对InnoDB的</span><code data-lake-id="u540eca52" id="u540eca52"><span data-lake-id="u6edf0057" id="u6edf0057">SELECT COUNT(*) FROM tbl_name</span></code><span data-lake-id="u32650e31" id="u32650e31">语句，确实在扫表的过程中做了一些优化。前提是查询语句中不包含WHERE或GROUP BY等条件。</span></p>
  <p data-lake-id="uec360195" id="uec360195"><br></p>
  <p data-lake-id="u6636d2cb" id="u6636d2cb"><span data-lake-id="u61fa170a" id="u61fa170a">我们知道，COUNT(*</span><em><span data-lake-id="u0d79d573" id="u0d79d573">)的目的只是为了统计总行数，所以，他根本不关心自己查到的具体值，所以，他如果能够在扫表的过程中，选择一个成本较低的索引进行的话，那就可以大大节省时间。</span></em></p>
  <p data-lake-id="u4bd080e4" id="u4bd080e4"><br></p>
  <p data-lake-id="u667d5894" id="u667d5894"><span data-lake-id="u39a1f261" id="u39a1f261">我们知道，InnoDB中索引分为聚簇索引（主键索引）和非聚簇索引（非主键索引），聚簇索引的叶子节点中保存的是整行记录，而非聚簇索引的叶子节点中保存的是该行记录的主键的值。</span></p>
  <p data-lake-id="uf13c24d9" id="uf13c24d9"><br></p>
  <p data-lake-id="ufb062446" id="ufb062446"><span data-lake-id="u9b3973e3" id="u9b3973e3">所以，相比之下，非聚簇索引要比聚簇索引小很多，所以</span><strong><span data-lake-id="u4f34082f" id="u4f34082f">MySQL会优先选择最小的非聚簇索引来扫表。所以，当我们建表的时候，除了主键索引以外，创建一个非主键索引还是有必要的。</span></strong></p>
  <p data-lake-id="u8cb88f4a" id="u8cb88f4a"><br></p>
  <p data-lake-id="ub855884f" id="ub855884f"><span data-lake-id="u3e12aef0" id="u3e12aef0">至此，我们介绍完了MySQL数据库对于COUNT(*)的优化，这些优化的前提都是查询语句中不包含WHERE以及GROUP BY条件。</span></p>
  <p data-lake-id="u53793c5b" id="u53793c5b"><br></p>
  <h2 data-lake-id="FEoPF" id="FEoPF"><span data-lake-id="u3fa9bbc7" id="u3fa9bbc7">COUNT(*)和COUNT(1)</span></h2>
  <p data-lake-id="u57fde8f2" id="u57fde8f2"><br></p>
  <p data-lake-id="ucdc257a4" id="ucdc257a4"><span data-lake-id="u1b25010c" id="u1b25010c">介绍完了</span><code data-lake-id="ue97a2a25" id="ue97a2a25"><span data-lake-id="u350c415d" id="u350c415d">COUNT(*)</span></code><span data-lake-id="ua11bca64" id="ua11bca64">，接下来看看</span><code data-lake-id="u498420c9" id="u498420c9"><span data-lake-id="ue059f936" id="ue059f936">COUNT(1)</span></code><span data-lake-id="ubb73c97e" id="ubb73c97e">，对于，这二者到底有没有区别，网上的说法众说纷纭。</span></p>
  <p data-lake-id="uc2d00f7e" id="uc2d00f7e"><br></p>
  <p data-lake-id="u8aa8e48d" id="u8aa8e48d"><span data-lake-id="ud1c5656b" id="ud1c5656b">有的说</span><code data-lake-id="ucc3f9796" id="ucc3f9796"><span data-lake-id="u58024208" id="u58024208">COUNT(*)</span></code><span data-lake-id="u5ded802f" id="u5ded802f">执行时会转换成</span><code data-lake-id="uc18258fd" id="uc18258fd"><span data-lake-id="u633e8d2d" id="u633e8d2d">COUNT(1)</span></code><span data-lake-id="udab92fa1" id="udab92fa1">，所以COUNT(1)少了转换步骤，所以更快。</span></p>
  <p data-lake-id="udcb92a8e" id="udcb92a8e"><br></p>
  <p data-lake-id="u7ecd965d" id="u7ecd965d"><span data-lake-id="ub698d787" id="ub698d787">还有的说，因为MySQL针对</span><code data-lake-id="u7852ed76" id="u7852ed76"><span data-lake-id="ue7b5471f" id="ue7b5471f">COUNT(*)</span></code><span data-lake-id="ue0a0c4bf" id="ue0a0c4bf">做了特殊优化，所以</span><code data-lake-id="u7b431506" id="u7b431506"><span data-lake-id="u9c2a10c4" id="u9c2a10c4">COUNT(*)</span></code><span data-lake-id="uda3edb47" id="uda3edb47">更快。</span></p>
  <p data-lake-id="u73a16fc8" id="u73a16fc8"><br></p>
  <p data-lake-id="u1e88b36c" id="u1e88b36c"><span data-lake-id="u648c31c9" id="u648c31c9">那么，到底哪种说法是对的呢？看下MySQL官方文档是怎么说的：</span></p>
  <p data-lake-id="u706f0d7f" id="u706f0d7f"><br></p>
  <blockquote data-lake-id="u5a6d3269" id="u5a6d3269">
   <p data-lake-id="u402c2ff9" id="u402c2ff9"><span data-lake-id="u08bc0a1a" id="u08bc0a1a">InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.</span></p>
  </blockquote>
  <p data-lake-id="uf374ffbe" id="uf374ffbe"><br></p>
  <p data-lake-id="u87d7cc5c" id="u87d7cc5c"><span data-lake-id="ua99ac223" id="ua99ac223">画重点：</span><code data-lake-id="u4d6ce52e" id="u4d6ce52e"><span data-lake-id="u6791dac0" id="u6791dac0">same way</span></code><span data-lake-id="ub6315505" id="ub6315505"> , </span><code data-lake-id="ua144880e" id="ua144880e"><span data-lake-id="u8151b5af" id="u8151b5af">no performance difference</span></code><span data-lake-id="ucdc0ab40" id="ucdc0ab40">。所以，对于COUNT(1)和COUNT(*</span><em><span data-lake-id="uccdcf0eb" id="uccdcf0eb">)，MySQL的优化是完全一样的，根本不存在谁比谁快！</span></em></p>
  <p data-lake-id="u3b584db7" id="u3b584db7"><br></p>
  <p data-lake-id="u495fbd5f" id="u495fbd5f"><span data-lake-id="uf652de3e" id="uf652de3e">那既然</span><code data-lake-id="udb8a7fa8" id="udb8a7fa8"><span data-lake-id="uf6ae743a" id="uf6ae743a">COUNT(*)</span></code><span data-lake-id="ua0a752a0" id="ua0a752a0">和</span><code data-lake-id="u1eeda683" id="u1eeda683"><span data-lake-id="ud76c17c2" id="ud76c17c2">COUNT(1)</span></code><span data-lake-id="u850c15d8" id="u850c15d8">一样，建议用哪个呢？</span></p>
  <p data-lake-id="u3495dd74" id="u3495dd74"><br></p>
  <p data-lake-id="u6712a4cc" id="u6712a4cc"><span data-lake-id="u89008672" id="u89008672">建议使用</span><code data-lake-id="u7c04b6fb" id="u7c04b6fb"><span data-lake-id="u5e0ea525" id="u5e0ea525">COUNT(*)</span></code><span data-lake-id="u7ff04c74" id="u7ff04c74">！因为这个是SQL92定义的标准统计行数的语法，而且本文只是基于MySQL做了分析，关于Oracle中的这个问题，也是众说纷纭的呢。</span></p>
  <p data-lake-id="u5d0a085d" id="u5d0a085d"><br></p>
  <h2 data-lake-id="G4Pnc" id="G4Pnc"><span data-lake-id="ub61e4bee" id="ub61e4bee">COUNT(字段)</span></h2>
  <p data-lake-id="ufb59467a" id="ufb59467a"><br></p>
  <p data-lake-id="u86b13905" id="u86b13905"><span data-lake-id="uf2b4387a" id="uf2b4387a">最后，就是我们一直还没提到的COUNT(字段)，他的查询就比较简单粗暴了，就是进行全表扫描，然后判断指定字段的值是不是为NULL，不为NULL则累加。</span></p>
  <p data-lake-id="u2453a4cc" id="u2453a4cc"><br></p>
  <p data-lake-id="u1e89671d" id="u1e89671d"><span data-lake-id="u4be4eb19" id="u4be4eb19">相比</span><code data-lake-id="ud3454409" id="ud3454409"><span data-lake-id="u00e5e16d" id="u00e5e16d">COUNT(*)</span></code><span data-lake-id="u2d1af50c" id="u2d1af50c">，</span><code data-lake-id="ufb139add" id="ufb139add"><span data-lake-id="u9a9615a9" id="u9a9615a9">COUNT(字段)</span></code><span data-lake-id="u7530f2b5" id="u7530f2b5">多了一个步骤就是判断所查询的字段是否为NULL，所以他的性能要比</span><code data-lake-id="ued883c24" id="ued883c24"><span data-lake-id="ucc4884d2" id="ucc4884d2">COUNT(*)</span></code><span data-lake-id="u1f467a19" id="u1f467a19">慢。</span></p>
 </body>
</html>